package gate

import (
	"container/list"
	"encoding/binary"
	"go_learn/actor"
	protos_user "go_learn/example/proto/protos.user"
	"go_learn/network"
)

type Gate struct {
	actor.ParallelActor
	MaxConnNum      int
	PendingWriteNum int
	Processor       network.Processor
	// tcp
	TCPAddr string

	agentIndex    uint
	agentIdlePool *list.List
	activeAgents  map[uint]*agent
	unauthAgents  map[uint]bool
	onlineUsers   map[uint64]*UserData
}

func (gate *Gate) Name() string {
	return "gate"
}

func (gate *Gate) OnInit() {
	gate.activeAgents = make(map[uint]*agent)
	gate.unauthAgents = make(map[uint]bool)
	gate.onlineUsers = make(map[uint64]*UserData)
	gate.agentIdlePool = list.New()
	gate.RegistHandle(actor.Normal, "login", gate.login)
	gate.RegistHandle(actor.Normal, "logout", gate.logout)
	gate.RegistHandle(actor.Normal, "kick", gate.kick)
	gate.Processor.Register(network.NewMsgInfo(
		&protos_user.RequestEnterGame{},
		"user",
		"RequestEnterGame",
		0))
}

func (gate *Gate) Run(args interface{}) {
	var tcpServer *network.TCPServer
	if gate.TCPAddr != "" {
		tcpServer = new(network.TCPServer)
		tcpServer.Addr = gate.TCPAddr
		tcpServer.MaxConnNum = gate.MaxConnNum
		tcpServer.PendingWriteNum = gate.PendingWriteNum
		tcpServer.NewAgent = gate.OnConnect

		if tcpServer != nil {
			encodeConfig := network.EncoderConfig{
				ByteOrder:         binary.LittleEndian,
				LengthFieldLength: 2,
			}
			decoderConfig := network.DecoderConfig{
				ByteOrder:           binary.LittleEndian,
				LengthFieldLength:   2,
				InitialBytesToStrip: 2,
			}
			c := network.NewLengthFieldBasedFrameCodec(encodeConfig, decoderConfig)
			tcpServer.Start(c)
		}
	}
}

func (gate *Gate) OnDestroy() {
}

func (gate *Gate) CloseAgent(fd uint) {
	if a, ok := gate.activeAgents[fd]; ok {
		delete(gate.activeAgents, fd)
		a.Close()
		gate.agentIdlePool.PushBack(fd)
	}
	if _, ok := gate.unauthAgents[fd]; ok {
		delete(gate.unauthAgents, fd)
	}
}

func (gate *Gate) OnConnect(conn *network.TCPConn) network.Agent {
	var index uint
	if gate.agentIdlePool.Len() <= 100 {
		gate.agentIndex++
		index = gate.agentIndex
	} else {
		element := gate.agentIdlePool.Front()
		index = element.Value.(uint)
		gate.agentIdlePool.Remove(element)
	}
	a := &agent{conn: conn, gate: gate, fd: index}
	gate.unauthAgents[index] = true
	gate.activeAgents[index] = a
	return a
}

func (gate *Gate) OnDisconnect(fd uint) {
	if a, ok := gate.activeAgents[fd]; ok {
		delete(gate.activeAgents, fd)
		if a.userdata != nil {
			a.userdata.fd = 0
		}
		gate.agentIdlePool.PushBack(fd)
	}
	if _, ok := gate.unauthAgents[fd]; ok {
		delete(gate.unauthAgents, fd)
	}
}
